use std::error::Error;

use apikit::ApiErrorItem;
use apikit::HandlerDef;
use apikit::StatusCode;
use axum::handler::Handler;
use axum::response::IntoResponse;
use axum::routing::MethodRouter;
use axum::routing::connect;
use axum::routing::delete;
use axum::routing::get;
use axum::routing::head;
use axum::routing::options;
use axum::routing::patch;
use axum::routing::post;
use axum::routing::put;
use axum::routing::trace;
use carpet_base::server::error_to_response;
use thiserror::Error;
use tracing::error;

pub type GetPathHandlerFn<T> = fn() -> PathHandler<T>;

pub trait MethodHandlerStraw {
    type S;

    fn path_hander_fn(&self) -> GetPathHandlerFn<Self::S>;
}

pub trait IntoPathHandler {
    fn into_path_handler<S: Clone + Send + Sync + 'static, A: 'static, H: Handler<A, S>>(
        h: H,
    ) -> PathHandler<S>;
}

impl<D: HandlerDef> IntoPathHandler for D {
    fn into_path_handler<S: Clone + Send + Sync + 'static, A: 'static, H: Handler<A, S>>(
        h: H,
    ) -> PathHandler<S> {
        let method = D::method();
        let method = method.as_str();
        let handler = match method {
            "DELETE" => delete(h),
            "GET" => get(h),
            "POST" => post(h),
            "HEAD" => head(h),
            "CONNECT" => connect(h),
            "OPTIONS" => options(h),
            "PATCH" => patch(h),
            "PUT" => put(h),
            "TRACE" => trace(h),
            _ => post(h),
        };
        PathHandler {
            layers: vec![],
            path: D::path().to_string(),
            handler,
        }
    }
}

pub struct PathHandler<S> {
    pub layers: Vec<String>,
    pub path: String,
    pub handler: MethodRouter<S>,
}

pub trait ExtractHandlerError: IntoResponse {
    fn from_error(e: Box<dyn Error + Send + Sync + 'static>) -> Self;
}

#[derive(Debug, Error)]
#[error("{0}")]
pub struct DefaultHandlerError(pub Box<dyn Error + Send + Sync + 'static>);

impl ExtractHandlerError for DefaultHandlerError {
    fn from_error(e: Box<dyn Error + Send + Sync + 'static>) -> Self {
        Self(e)
    }
}

impl IntoResponse for DefaultHandlerError {
    fn into_response(self) -> axum::response::Response {
        error!("状态提取错误：{self:?}");
        let e = StatusCode::INTERNAL_SERVER_ERROR;
        let e = ApiErrorItem::from(e);
        error_to_response(e)
    }
}
